package com.share.netdisk.user.service.impl;

import cn.hutool.captcha.CaptchaUtil;
import cn.hutool.captcha.LineCaptcha;
import cn.hutool.captcha.generator.RandomGenerator;
import cn.hutool.crypto.digest.DigestUtil;

import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.dysmsapi.model.v20170525.SendSmsRequest;
import com.aliyuncs.dysmsapi.model.v20170525.SendSmsResponse;
import com.aliyuncs.profile.DefaultProfile;
import com.aliyuncs.profile.IClientProfile;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.share.constant.DefaultConstant;
import com.share.constant.Result;
import com.share.constant.StatusCode;
import com.share.holder.ContextHolder;
import com.share.holder.UUIDHolder;
import com.share.netdisk.file.dao.FileDao;
import com.share.netdisk.file.pojo.UserFile;
import com.share.netdisk.file.service.FileService;
import com.share.netdisk.user.dao.UserDao;
import com.share.netdisk.user.model.User;

import com.share.netdisk.user.service.UserService;
import com.share.tdo.UserLoginTdo;
import com.share.tdo.UserRegisterTdo;
import com.share.utils.AliYunParameterUtil;
import com.share.utils.JWTUtil;
import com.share.utils.RegularUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

import static com.share.constant.DefaultConstant.*;


@Service
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserDao, User> implements UserService {

    @Autowired
    private UserDao userDao;

    @Autowired
    private RedisTemplate redisTemplate;


    @Value("${hdfs.uri}")
    private String hdfsUri;

    @Value("${hdfs.blocksize}")
    private String hdfsBlocksize;

    @Value("${hdfs.user}")
    private String hdfsUser;

    private static final Configuration conf = new Configuration();

    @Autowired
    private FileService fileService;

    private FileSystem fs;

    @PostConstruct
    public void init() throws URISyntaxException, IOException, InterruptedException {
        // 使用 @Value 注入的属性设置 Configuration
        conf.set("fs.defaultFS", hdfsUri);
        conf.set("dfs.blocksize", hdfsBlocksize);
        fs = FileSystem.get(new URI(hdfsUri), conf, hdfsUser);
    }

    @PreDestroy
    public void cleanup() {
        if (fs != null) {
            try {
                fs.close();
                log.info("FileSystem关闭");
            } catch (IOException e) {
                log.error("未能关闭FileSystem", e);
            }
        }
    }
    //账号密码登录
    @Override
    public Result login(UserLoginTdo user) {
        //校验验证码
        if (!validateCaptcha()) {
            return Result.fail(StatusCode.CAPTCHA_ERROR.getCode(), StatusCode.CAPTCHA_ERROR.getMessage());
        }

        // 查询用户
        User dbUser = userDao.selectOne(new LambdaQueryWrapper<User>().eq(User::getUsername, user.getUsername()));
        if (dbUser == null) {
            return Result.fail(StatusCode.USERNAME_NOT_EXIST.getCode(), StatusCode.USERNAME_NOT_EXIST.getMessage());
        }

        // 校验密码
        if (!passwordEncrypt(user.getPassword()).equals(dbUser.getPassword())) {
            return Result.fail(StatusCode.PASSWORD_ERROR.getCode(), StatusCode.PASSWORD_ERROR.getMessage());
        }

        // 登陆成功，生成并返回token
        return loginSuccess(dbUser.getId());

    }

    // 手机号登录
    @Override
    public Result loginByPhone(String phone, String code) {
        log.info("phone: {}, code: {}", phone, code);

        if (!RegularUtil.isPhone(phone)) {
            return Result.fail(StatusCode.PHONE_FORMAT_ERROR.getCode(), StatusCode.PHONE_FORMAT_ERROR.getMessage());
        }

        // 验证码检查
        if (!validateCaptchaPhone(phone, code)) {
            return Result.fail(StatusCode.CAPTCHA_ERROR.getCode(), StatusCode.CAPTCHA_ERROR.getMessage());
        }

        // 查询用户
        User dbUser = userDao.selectOne(new LambdaQueryWrapper<User>().eq(User::getPhone, phone) .eq(User::getStatus, 1) );

        // 用户存在则直接登录
        if (dbUser != null) {
            log.info("用户存在，直接登录");
            return loginSuccess(dbUser.getId());
        }

        // 用户不存在，则注册新用户
        return registerAndLogin(phone, code);
    }

    @Override
    public Result getUserInfo() {
        Long userId = ContextHolder.get();
        if (userId != null) {
            User user = userDao.selectById(userId);
            User userInfo = new User();
            userInfo.setUsername(user.getUsername());
            userInfo.setPhotoUrl(user.getPhotoUrl());
            return Result.ok(userInfo);
        }
        return Result.fail(StatusCode.USER_NOT_LOGIN.getCode(), StatusCode.USER_NOT_LOGIN.getMessage());
    }

    @Override
    public String getCaptcha(String phone) {
        SendSmsResponse sendSmsResponse = null;
        // 1. 准备好短信模板变量 —— 验证码 code
        int code = (int) ((Math.random() * 9000) + 1000);
        log.info("手机号验证码" + code);
        redisTemplate.opsForValue().set("phoneCodes:" + phone, String.valueOf(code), 5, TimeUnit.MINUTES);

        try {
            // 2. 可自助调整超时时间
            System.setProperty("sun.net.client.defaultConnectTimeout", "10000");
            System.setProperty("sun.net.client.defaultReadTimeout", "10000");

            // 3. 初始化acsClient,暂不支持region化
            IClientProfile profile = DefaultProfile.getProfile("cn-hangzhou", AliYunParameterUtil.accessKeyId, AliYunParameterUtil.accessKeySecret);
            DefaultProfile.addEndpoint("cn-hangzhou", "cn-hangzhou", AliYunParameterUtil.PRODUCT, AliYunParameterUtil.DOMAIN);
            IAcsClient acsClient = new DefaultAcsClient(profile);

            //4.组装请求对象-具体描述见控制台-文档部分内容
            SendSmsRequest request = new SendSmsRequest();

            //必填:待发送手机号
            request.setPhoneNumbers(phone);
            //必填:短信签名-可在短信控制台中找到
            request.setSignName(AliYunParameterUtil.signName);
            //必填:短信模板-可在短信控制台中找到
            request.setTemplateCode(AliYunParameterUtil.templateCode);
            //可选:模板中的变量替换JSON串,如模板内容为"亲爱的${name},您的验证码为${code}"时,此处的值为
            request.setTemplateParam(String.format("{\"code\":\"%s\"}", code));     // 这里是 json

            //5.hint 此处可能会抛出异常，注意catch
            sendSmsResponse = acsClient.getAcsResponse(request);
            if (sendSmsResponse != null && "OK".equals(sendSmsResponse.getCode())) {
                log.info("短信发送成功！");
                redisTemplate.opsForValue().set(phone, String.valueOf(code), 5, TimeUnit.MINUTES);       // 将手机号作为 key 放入验证码  有效期五分钟
            } else {
                log.error("短信发送失败！错误码: {}, 错误信息: {}",
                        sendSmsResponse == null ? "未知" : sendSmsResponse.getCode(),
                        sendSmsResponse == null ? "未知" : sendSmsResponse.getMessage());
                throw new RuntimeException("短信发送失败");
            }

        } catch (Exception e) {
            System.out.println("引起异常的原因------>" + e);
            System.out.println("异常信息------>" + e.getMessage());
            System.out.println("短信发送失败！");
            throw new IllegalArgumentException("手机号码不能为空");
        }
        return String.valueOf(code);

    }

    // 校验手机号验证码
    public boolean verifyPhoneCode(String phone, String code) {
        // 1. 拿到 redis中存储的手机号验证码
        String authCode = (String)redisTemplate.opsForValue().get(USER_LOGIN_PHONE + phone);

        // 2. 如果验证码过期
        if (authCode == null) {
            throw new IllegalArgumentException("验证码已过期或不存在");
        }
        log.info("正确的手机号验证码" + authCode);

        // 3. 校验验证码的有效性
        if (code == null || code.isEmpty() || !code.equals(authCode)) {
            return false;
        }

        // 4. 验证成功将该验证码删掉
//        redis.delete("phoneCodes:" + phone);
        return true;
    }


    // 登录成功统一返回token
    private Result loginSuccess(Long userId) {
        String token = JWTUtil.generateToken(userId);
        redisTemplate.opsForValue().set(USER_LOGIN_KEY + userId, token, 1, TimeUnit.HOURS);
        return Result.ok("登录成功", token);
    }
    // 注册并登录
    private Result registerAndLogin(String phone, String code) {
        User newUser = new User();
        newUser.setPhone(phone);
        newUser.setUsername(phone);
        //默认密码123456,加密后存储
        newUser.setPassword(passwordEncrypt("123456"));

        int insertResult = userDao.insert(newUser);
        if (insertResult <= 0) {
            log.error("用户插入数据库失败");
            return Result.fail(StatusCode.REGISTER_FAIL.getCode(), StatusCode.REGISTER_FAIL.getMessage());
        }

        Long userId = newUser.getId();
        log.warn("userID: {}", userId);

        String homeDir = "/user" + userId + "/home/";
        String recycleDir = "/user" + userId + "/recycle/";
        String shareDir = "/user" + userId + "/share/";
        //创建用户家目录以及回收站
        boolean dirs = createDirs(new String[]{homeDir, recycleDir, shareDir});
        if(dirs!= true){
            throw new RuntimeException("创建用户家目录以及回收站失败");
        }
        // TODO 创建成功后将数据插入数据库
        UserFile userFile = new UserFile(userId, HOME,homeDir,RECLAIM,recycleDir,SHARE,shareDir);
        boolean insert = fileService.insertHomeFile(userFile);
        if(!insert){
            log.warn("插入用户家目录失败");
            throw new RuntimeException("插入用户家目录失败");
        }

        String token = JWTUtil.generateToken(userId);
        redisTemplate.opsForValue().set(USER_LOGIN_KEY + userId, token, 1, TimeUnit.HOURS);
        return Result.ok("注册并登录成功", token);
    }

    private boolean createDirs(String[] dirs) {
        for (String dir : dirs) {
            Path newDir = new Path(dir);
            try {
                if (!fs.mkdirs(newDir)) {
                    // 如果创建失败，记录日志或采取其他措施（可选）
                    System.err.println("文件创建失败: " + dir);
                    return false;
                }
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
        return true;
    }
    //注册
    @Override
    public Result register(UserRegisterTdo user) {
     //1.获取手机号，查询此手机号是否已经注册
        String phone = user.getPhone();
        //2.根据手机号查询用户是否存在
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(User::getPhone, phone) .eq(User::getStatus, 1) ;
        if (userDao.selectOne(wrapper) != null) {
            return Result.fail(StatusCode.PHONE_EXIST.getCode(), StatusCode.PHONE_EXIST.getMessage());
        }
        //3.根据手机号从redis中获取验证码
        String captcha = (String) redisTemplate.opsForValue().get(USER_LOGIN_PHONE + phone);
        if (captcha == null || !captcha.equals(user.getCode())) {
            return Result.fail(StatusCode.CAPTCHA_ERROR.getCode(), StatusCode.CAPTCHA_ERROR.getMessage());
        }
        //4.保存用户信息到数据库中
        user.setPassword(passwordEncrypt(user.getPassword()));
        User newUser = new User(user.getUsername(), user.getPassword(), phone);

        int insert = userDao.insert(newUser);
        if (insert <= 0) {
            return Result.fail(StatusCode.REGISTER_FAIL.getCode(), StatusCode.REGISTER_FAIL.getMessage());
        }
        return Result.ok("注册成功");

    }


    //用户是否存在
    @Override
    public Result isExist(String username) {
        //先确定是否为空
        if (username == null || "".equals(username)) {
            throw new IllegalArgumentException("用户名不能为空");
        }
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(User::getUsername, username) .eq(User::getStatus, 1) ;
        User user = userDao.selectOne(wrapper);
        if (user != null) {
            return Result.fail(StatusCode.USERNAME_EXIST.getCode(), StatusCode.USERNAME_EXIST.getMessage());
        } else {
            return Result.ok("用户名可用");
        }
    }


    //密码加密方法
    private String passwordEncrypt(String password) {
        return DigestUtil.sha256Hex(password + DefaultConstant.SALT);
    }

    //TODO 手机号验证码校验,验证码服务器还未完成
    private boolean validateCaptchaPhone(String phone, String code) {
        String captcha = (String) redisTemplate.opsForValue().get(USER_LOGIN_PHONE + phone);
        return captcha != null && captcha.equals(code);
    }

    // 生成验证码文本和图片
    @Override
    public ResponseEntity<byte[]> generateCaptcha() {
        // 定义字符集
        String charSet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
        // 创建自定义的随机数生成器
        RandomGenerator randomGenerator = new RandomGenerator(charSet, 4);
        // 创建LineCaptcha对象
        LineCaptcha lineCaptcha = CaptchaUtil.createLineCaptcha(200, 100);
        lineCaptcha.setGenerator(randomGenerator);
        // 重新生成验证码
        lineCaptcha.createCode();
        // 获取验证码文本
        String code = lineCaptcha.getCode();
        System.out.println("生成code: " + code);
        // 生成唯一标识符
        String uuid = UUID.randomUUID().toString().replace("-", "");
        System.out.println("uuid: " + uuid);
        // 将验证码存入Redis，并设置有效期为1分钟
        redisTemplate.opsForValue().set(uuid, code, 5, TimeUnit.MINUTES);

        // 将验证码图片转换为字节数组
        byte[] imageBytes = lineCaptcha.getImageBytes();

        // 设置响应头
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.IMAGE_JPEG);
        headers.set(X_CAPTCHA_UUID, uuid);

        return new ResponseEntity<>(imageBytes, headers, 200);
    }

    // 校验验证码
    @Override
    public boolean validateCaptcha() {
        String captchaHeader = UUIDHolder.get();
        //按“-”分割得到uuid和code
        String[] split = captchaHeader.split("-");
        String uuid  = split[0];
        String code = split[1];
        // 从Redis获取验证码并进行比较
        String storedCode = (String) redisTemplate.opsForValue().get(uuid);
        System.out.println("storedCode: " + storedCode);
        System.out.println("code: " + code);

        // 验证码不存在或不匹配时立即返回失败结果
        if (storedCode == null && !storedCode.equalsIgnoreCase(code)) {
            return false;
        }
        return true;
    }

    //    注销
    @Override
    @Transactional
    public Result logout() {
        //获取当前用户id
        Long userId = ContextHolder.get();
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(User::getId, userId) .eq(User::getStatus, 1) ;
        User user = userDao.selectOne(wrapper);
        if (user == null) {
            return Result.fail(StatusCode.USERNAME_NOT_EXIST.getCode(), StatusCode.USERNAME_NOT_EXIST.getMessage());
        }
        //将数据库用户状态status改为0
        user.setStatus(0);
        int i = 0;
        try {
            i = userDao.updateById(user);
            //删除redis中的token
            redisTemplate.delete(USER_LOGIN_KEY + userId);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        if(i <= 0){
            return Result.fail(StatusCode.LOGOUT_FAIL.getCode(), StatusCode.LOGOUT_FAIL.getMessage());
        }
        return Result.ok("注销成功");
    }

}
